package com.cjj.service.impl;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cjj.common.convention.exception.ClientException;
import com.cjj.common.enums.PostCategory;
import com.cjj.common.enums.PostStatus;
import com.cjj.common.enums.PostType;
import com.cjj.dao.entity.ForumPost;
import com.cjj.dao.entity.User;
import com.cjj.dao.mapper.ForumPostMapper;
import com.cjj.dto.req.GetPublishedPostsReqDTO;
import com.cjj.dto.req.PostCreateReqDTO;
import com.cjj.dto.req.ReviewPostReqDTO;
import com.cjj.dto.resp.CommentVO;
import com.cjj.dto.resp.ForumPostDetailRespDTO;
import com.cjj.service.ForumPostService;
import com.cjj.service.PostCommentService;
import com.cjj.service.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class ForumPostServiceImpl extends ServiceImpl<ForumPostMapper, ForumPost> implements ForumPostService {


    private final UserService userService;
    private final StringRedisTemplate redisTemplate;
    private final PostCommentService postCommentService;

    private static final String HOT_POST_KEY = "forum:hot:posts";
    /**
     *分页查询待审核的订单
     * @return
     */
    @Override
    public Page<ForumPost> getPendingPosts(Integer pageNum, Integer pageSize) {
        // 校验分页参数
        pageNum = (pageNum == null || pageNum < 1) ? 1 : pageNum;
        pageSize = (pageSize == null || pageSize < 1) ? 10 : pageSize;
        pageSize = Math.min(pageSize, 100); // 限制最大页大小

        //创建分页对象
        Page<ForumPost> page = new Page<>(pageNum, pageSize);

        // 构建查询条件
        LambdaQueryWrapper<ForumPost> lambdaQueryWrapper = Wrappers.lambdaQuery(ForumPost.class)
                .eq(ForumPost::getStatus, PostStatus.PENDING.getValue());

        //查询数据库
        Page<ForumPost> forumPostPage = baseMapper.selectPage(page, lambdaQueryWrapper);
        return forumPostPage;
    }

    /**
     * 审核帖子
     */
    @Transactional
    @Override
    public void reviewPost(ReviewPostReqDTO reviewPostReqDTO) {
        // 参数校验
        if (reviewPostReqDTO == null) {
            throw new ClientException("审核参数不能为空");
        }
        if (reviewPostReqDTO.getPostId() == null || reviewPostReqDTO.getPostId().isEmpty()) {
            throw new ClientException("帖子ID列表不能为空");
        }
        if (reviewPostReqDTO.getReviewerId() == null) {
            throw new ClientException("审核人ID不能为空");
        }
        if (!"PUBLISHED".equals(reviewPostReqDTO.getStatus()) && !"REJECTED".equals(reviewPostReqDTO.getStatus())) {
            throw new ClientException("状态参数错误，只能为PUBLISHED或REJECTED");
        }
        if ("REJECTED".equals(reviewPostReqDTO.getStatus()) && StringUtils.isBlank(reviewPostReqDTO.getRejectReason())) {
            throw new ClientException("拒绝帖子必须填写拒绝原因");
        }

        // 权限校验
        User user = userService.getById(reviewPostReqDTO.getReviewerId());
        if (user == null || user.getUserType() != 2) {
            throw new ClientException("当前用户非管理员，无审核权限");
        }

        // 批量更新帖子状态
        LambdaUpdateWrapper<ForumPost> updateWrapper = Wrappers.lambdaUpdate(ForumPost.class)
                .in(ForumPost::getPostId, reviewPostReqDTO.getPostId())
                .eq(ForumPost::getStatus, "pending") // 只更新待审核状态的帖子
                .set(ForumPost::getStatus, reviewPostReqDTO.getStatus());

        // 如果是拒绝，设置拒绝原因
        if ("rejected".equals(reviewPostReqDTO.getStatus())) {
            updateWrapper.set(ForumPost::getRejectReason, reviewPostReqDTO.getRejectReason());
        }

        int updateCount = baseMapper.update(null, updateWrapper);

        if (updateCount == 0) {
            throw new ClientException("未找到需要审核的帖子或帖子状态不符合要求");
        }
    }

    /**
     * 创建帖子（默认待审核状态）
     */
    @Transactional
    @Override
    public void createPost(PostCreateReqDTO postCreateReqDTO) {
        //校验参数
        if (!PostCategory.isValid(postCreateReqDTO.getCategory())){
            throw new ClientException("PostCategory类别不存在");
        }
        if (!PostType.isValid(postCreateReqDTO.getPostType())){
            throw new ClientException("PostType类别不存在");
        }
        ForumPost post = new ForumPost();
        post.setUserId(postCreateReqDTO.getUserId())
                .setTitle(postCreateReqDTO.getTitle())
                .setContent(postCreateReqDTO.getContent())
                .setPostType(postCreateReqDTO.getPostType())
                .setCategory(postCreateReqDTO.getCategory())
                .setViewCount(0)
                .setLikeCount(0)
                .setCommentCount(0)
                .setIsTop(false)
                .setIsEssence(false)
                .setStatus(PostStatus.PENDING.getValue()); // 默认待审核

        baseMapper.insert(post);
    }

    /**
     * 获取帖子列表（只返回已发布的）
     */
    @Override
    public Page<ForumPost> getPublishedPosts(Integer pageNum, Integer pageSize, GetPublishedPostsReqDTO getPublishedPostsReqDTO) {
        // 校验分页参数
        pageNum = (pageNum == null || pageNum < 1) ? 1 : pageNum;
        pageSize = (pageSize == null || pageSize < 1) ? 10 : pageSize;
        pageSize = Math.min(pageSize, 100); // 限制最大页大小

        //创建分页对象
        Page<ForumPost> page = new Page<>(pageNum, pageSize);

        // 构建查询条件
        LambdaQueryWrapper<ForumPost> lambdaQueryWrapper = Wrappers.lambdaQuery(ForumPost.class)
                .eq(ForumPost::getStatus, PostStatus.PUBLISHED.getValue())
                .eq(StrUtil.isNotBlank(getPublishedPostsReqDTO.getPostType()),ForumPost::getPostType, getPublishedPostsReqDTO.getPostType())
                .eq(StrUtil.isNotBlank(getPublishedPostsReqDTO.getCategory()),ForumPost::getCategory, getPublishedPostsReqDTO.getCategory());

        //查询数据库
        Page<ForumPost> forumPostPage = baseMapper.selectPage(page, lambdaQueryWrapper);
        return forumPostPage;

    }

    /**
     * 查看帖子详情同时更新帖子浏览量
     */
    @Transactional
    @Override
    public ForumPostDetailRespDTO getPostDetail(Long postId, Long currentUserId) {
        // 1. 查询帖子是否存在且已发布
        ForumPost post = baseMapper.selectById(postId);
        if (post == null) {
            throw new ClientException("帖子不存在");
        }
        if (!PostStatus.PUBLISHED.getValue().equals(post.getStatus())) {
            throw new ClientException("帖子未发布或已被删除");
        }

        // 2. 更新浏览数（线程安全）
        LambdaUpdateWrapper<ForumPost> updateWrapper = Wrappers.lambdaUpdate(ForumPost.class)
                .eq(ForumPost::getPostId, postId)
                .setSql("view_count = view_count + 1");
        baseMapper.update(null, updateWrapper);

        // 3. 记录访问（用于热点计算）
        recordPostAccess(postId);

        // 4. 转换为DTO并设置扩展字段
        ForumPostDetailRespDTO detailDTO = convertToDetailDTO(post, currentUserId);

        return detailDTO;
    }

    /**
     * 转换为详情DTO
     */
    private ForumPostDetailRespDTO convertToDetailDTO(ForumPost post, Long currentUserId) {
        ForumPostDetailRespDTO dto = new ForumPostDetailRespDTO();
        // 复制基础属性
        BeanUtil.copyProperties(post, dto);

        // 设置作者信息
        setAuthorInfo(dto, post.getUserId());

        // 设置点赞状态
        setLikeStatus(dto, post.getPostId(), currentUserId);

        // 设置评论列表
        setCommentList(dto, post.getPostId(), currentUserId);

        return dto;
    }

    /**
     * 设置作者信息
     */
    private void setAuthorInfo(ForumPostDetailRespDTO dto, Long userId) {
        try {
            User user = userService.getById(userId);
            if (user != null) {
                dto.setAuthorName(user.getName());
                // 如果有头像字段的话
                // dto.setAuthorAvatar(user.getAvatar());
            }
        } catch (Exception e) {
            log.error("获取用户信息失败, userId: {}", userId, e);
            dto.setAuthorName("未知用户");
        }
    }

    /**
     * 设置点赞状态
     */
    private void setLikeStatus(ForumPostDetailRespDTO dto, Long postId, Long currentUserId) {
        try {
            if (currentUserId != null) {
                // 需要实现点赞服务后取消注释
                // dto.setHasLiked(postLikeService.hasLiked(postId, currentUserId));
                dto.setHasLiked(false); // 暂时设为false
            } else {
                dto.setHasLiked(false);
            }
        } catch (Exception e) {
            log.error("获取点赞状态失败, postId: {}, userId: {}", postId, currentUserId, e);
            dto.setHasLiked(false);
        }
    }

    /**
     * 设置评论列表
     */
    private void setCommentList(ForumPostDetailRespDTO dto, Long postId, Long currentUserId) {
        try {

            List<CommentVO> comments = postCommentService.getPostComments(postId, currentUserId);
            dto.setComments(comments);

        } catch (Exception e) {
            log.error("获取评论列表失败, postId: {}", postId, e);
            dto.setComments(new ArrayList<>());
        }
    }

    /**
     * 记录帖子访问（用于热点计算）
     */
    private void recordPostAccess(Long postId) {
        try {
            String accessKey = "forum:post:access:" + LocalDate.now().toString();
            redisTemplate.opsForZSet().incrementScore(accessKey, postId.toString(), 1);
            // 设置过期时间（保留3天）
            redisTemplate.expire(accessKey, Duration.ofDays(3));
        } catch (Exception e) {
            log.error("记录帖子访问失败, postId: {}", postId, e);
        }
    }


    /**
    * 每10分钟执行一次
     */
    @Scheduled(fixedRate = 600_000)
    public void refreshHotPosts() {
        // 1️.获取需要更新的帖子ID集合
        Set<Long> targetIds = getTargetPostIds();

        if (targetIds.isEmpty()) return;

        // 2️.批量查询帖子详情
        List<ForumPost> posts = baseMapper.selectBatchIds(targetIds);

        // 3️.计算热度并写入Redis
        posts.forEach(post -> {
            double score = calculateHotScore(post);
            redisTemplate.opsForZSet().add(HOT_POST_KEY, post.getPostId().toString(), score);
        });
    }

    /**
     * 热度分数计算公式：时间衰减
     */
    private double calculateHotScore(ForumPost post) {
        long hoursSincePost = Duration.between(post.getCreateTime(), LocalDateTime.now()).toHours();
        double baseScore = post.getViewCount() * 0.5
                + post.getLikeCount() * 2
                + post.getCommentCount() * 3;
        return baseScore / Math.pow((hoursSincePost + 2), 1.5);
    }

    /**
     * 获取最近活跃和当前热榜前50的帖子ID
     */
    private Set<Long> getTargetPostIds() {
        Set<Long> ids = new HashSet<>();

        // 最近10分钟内活跃的帖子（update_time变化）
        List<ForumPost> activePosts = baseMapper.selectList(new LambdaQueryWrapper<ForumPost>()
                .eq(ForumPost::getStatus, "published")
                .ge(ForumPost::getUpdateTime, LocalDateTime.now().minusMinutes(100)));

        ids.addAll(activePosts.stream()
                .map(ForumPost::getPostId)
                .collect(Collectors.toSet()));

        // 当前热榜前50帖子
        Set<String> hotIds = redisTemplate.opsForZSet().reverseRange(HOT_POST_KEY, 0, 49);
        if (hotIds != null) {
            hotIds.forEach(id -> ids.add(Long.valueOf(id)));
        }

        return ids;
    }

    /**
     * 获取热榜前 N 条帖子ID
     */
    public List<Long> getTopHotPostIds(int limit) {
        Set<String> ids = redisTemplate.opsForZSet().reverseRange(HOT_POST_KEY, 0, limit - 1);
        if (ids == null) return Collections.emptyList();
        return ids.stream().map(Long::parseLong).collect(Collectors.toList());
    }
}
